package jinyilw.swing;
import jinyilw.common.Operator;
import jinyilw.swing.visuals.FlatDialog;
import jinyilw.swing.visuals.FlatFrame;

import javax.swing.JComponent;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.IllegalComponentStateException;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Window;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
public class DragCompUtils
{
	public static final int[] cursorMapping = ComponentTools.cursorMapping;
	public static final Dimension MINIMUM_SIZE = new Dimension(120, 80);
	public static final int CORNER_DRAG_WIDTH = 16;
	public static final int BORDER_DRAG_THICKNESS = 5;
	private static boolean componentMoved = false;
	private static boolean movable = true;
	// private static boolean resizeable = false;
	private static boolean top = false, down = false, left = false,
			right = false;
	private static Point pressedPoint;
	private static Cursor savedCursor;
	private static int cursorIndex = 0;
	private static int dragOffsetX, dragOffsetY;
	private static int dragWidth, dragHeight;

	private DragCompUtils()
	{
	}

	public static void setMovable(boolean b)
	{
		movable = b;
	}

	public static void setMovable(final Component dragCom,
			final Component moveCom)
	{
		if (dragCom == null || moveCom == null)
			return;
		dragCom.addMouseListener(new MouseAdapter()
		{
			@Override
			public void mousePressed(MouseEvent e)
			{
				pressedPoint = convertPoint(dragCom, e.getPoint(), moveCom);
			}
		});
		dragCom.addMouseMotionListener(new MouseMotionAdapter()
		{
			@Override
			public void mouseDragged(MouseEvent e)
			{
				if (movable && cursorIndex == 0 && pressedPoint != null)
				{
					componentMoved = true;
					Point location;
					if (moveCom instanceof Window)
						location = e.getLocationOnScreen();
					else
						location = convertPoint(dragCom, e.getPoint(),
								moveCom.getParent());
					location.x -= pressedPoint.x;
					location.y -= pressedPoint.y;
					moveCom.setLocation(location);
				}
			}
		});
	}

	public static void addMoveReleaseOperator(final Component component,
			final Operator<?> operator)
	{
		componentMoved = false;
		component.addMouseListener(new MouseAdapter()
		{
			@Override
			public void mouseReleased(MouseEvent e)
			{
				if (componentMoved)
					operator.operate(null);
				componentMoved = false;
			}
		});
	}

	public static Point convertPoint(Component source, Point aPoint,
			Component destination)
	{
		Point p;

		if (source == destination)
			return aPoint;
		if (source == null)
		{
			source = WindowTools.getWindowAncestor(destination);
			if (source == null)
				throw new Error(
						"Source component not connected to component tree hierarchy");
		}
		p = new Point(aPoint);
		convertPointToScreen(p, source);
		if (destination == null)
		{
			destination = WindowTools.getWindowAncestor(source);
			if (destination == null)
				throw new Error(
						"Destination component not connected to component tree hierarchy");
		}
		convertPointFromScreen(p, destination);
		return p;
	}

	public static void convertPointToScreen(Point p, Component c)
	{
		int x, y;
		do
		{
			if (c instanceof JComponent)
			{
				x = c.getX();
				y = c.getY();
			} else if (c instanceof Window)
			{
				try
				{
					Point pp = c.getLocationOnScreen();
					x = pp.x;
					y = pp.y;
				} catch (IllegalComponentStateException icse)
				{
					x = c.getX();
					y = c.getY();
				}
			} else
			{
				x = c.getX();
				y = c.getY();
			}
			p.x += x;
			p.y += y;
			if (c instanceof Window)
				break;
			c = c.getParent();
		} while (c != null);
	}

	public static void convertPointFromScreen(Point p, Component c)
	{
		int x, y;
		do
		{
			if (c instanceof JComponent)
			{
				x = c.getX();
				y = c.getY();
			} else if (c instanceof Window)
			{
				try
				{
					Point pp = c.getLocationOnScreen();
					x = pp.x;
					y = pp.y;
				} catch (IllegalComponentStateException icse)
				{
					x = c.getX();
					y = c.getY();
				}
			} else
			{
				x = c.getX();
				y = c.getY();
			}
			p.x -= x;
			p.y -= y;
			if (c instanceof Window)
				break;
			c = c.getParent();
		} while (c != null);
	}

	public static void setResizeable(final Component dragCom,
			final Component resizeCom)
	{
		if (dragCom == null || resizeCom == null)
			return;
		dragCom.addMouseMotionListener(new MouseAdapter()
		{
			@Override
			public void mouseMoved(MouseEvent e)
			{
				resizeCom.setCursor(
						Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
				top = false;
				down = false;
				left = false;
				right = false;
				if (e.getY() <= 5)
				{
					resizeCom.setCursor(
							Cursor.getPredefinedCursor(Cursor.N_RESIZE_CURSOR));
					top = true;
				} else if (resizeCom.getHeight() - e.getY() <= 5)
				{
					resizeCom.setCursor(
							Cursor.getPredefinedCursor(Cursor.N_RESIZE_CURSOR));
					down = true;
				} else if (e.getX() <= 5)
				{
					resizeCom.setCursor(
							Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR));
					left = true;
				} else if (resizeCom.getWidth() - e.getX() <= 5)
				{
					resizeCom.setCursor(
							Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR));
					right = true;
				}
			}

			@Override
			public void mouseDragged(MouseEvent e)
			{
				int w = resizeCom.getWidth(), h = resizeCom.getHeight();
				Point location = resizeCom.getLocationOnScreen();
				if (top)
				{
					resizeCom.setSize(w, h - e.getY());
					resizeCom.setLocation(location.x, location.y + e.getY());
				} else if (down)
					resizeCom.setSize(w, e.getY());
				else if (left)
				{
					resizeCom.setSize(w - e.getX(), h);
					resizeCom.setLocation(location.x + e.getX(), location.y);
				} else if (right)
					resizeCom.setSize(e.getX(), h);
			}
		});

		dragCom.addMouseListener(new MouseAdapter()
		{
			FlatDialog dialog = null;
			FlatFrame frame = null;
			boolean b;

			@Override
			public void mousePressed(MouseEvent e)
			{
				if (top || down || left || right)
				{
					movable = false;
					if (resizeCom instanceof FlatDialog)
					{
						dialog = (FlatDialog) resizeCom;
						b = dialog.isRounded();
						if (b)
							dialog.setRounded(false);
					} else if (resizeCom instanceof FlatFrame)
					{
						frame = (FlatFrame) resizeCom;
						b = frame.isRounded();
						if (b)
							frame.setRounded(false);
					}
				}
			}

			@Override
			public void mouseReleased(MouseEvent e)
			{
				movable = true;
				if (resizeCom instanceof FlatDialog && dialog != null)
					dialog.setRounded(b);
				else if (resizeCom instanceof FlatFrame && frame != null)
					frame.setRounded(b);
			}
		});
	}

	public static void setResizeable2(final Component dragCom,
			final Component resizeCom)
	{

		dragCom.addMouseMotionListener(new MouseAdapter()
		{
			@Override
			public void mouseMoved(MouseEvent e)
			{
				cursorIndex = getCursor(
						calculateCorner(resizeCom, e.getX(), e.getY()));
				if (cursorIndex != 0)
				{
					if (savedCursor == null)
						savedCursor = resizeCom.getCursor();
					resizeCom
							.setCursor(Cursor.getPredefinedCursor(cursorIndex));
				} else if (savedCursor != null)
				{
					resizeCom.setCursor(savedCursor);
					savedCursor = null;
				}
			}

			@Override
			public void mouseDragged(MouseEvent e)
			{
				if (cursorIndex != 0)
				{
					Point pt = e.getPoint();
					Rectangle bounds = resizeCom.getBounds();
					Rectangle startBounds = new Rectangle(bounds);
					Dimension min = MINIMUM_SIZE;
					switch (cursorIndex)
					{
						case Cursor.E_RESIZE_CURSOR:
							adjust(bounds, min, 0, 0, pt.x
											+ (dragWidth - dragOffsetX) - bounds.width,
									0);
							break;
						case Cursor.S_RESIZE_CURSOR:
							adjust(bounds, min, 0, 0, 0,
									pt.y + (dragHeight - dragOffsetY)
											- bounds.height);
							break;
						case Cursor.N_RESIZE_CURSOR:
							adjust(bounds, min, 0, pt.y - dragOffsetY, 0,
									-(pt.y - dragOffsetY));
							break;
						case Cursor.W_RESIZE_CURSOR:
							adjust(bounds, min, pt.x - dragOffsetX, 0,
									-(pt.x - dragOffsetX), 0);
							break;
						case Cursor.NE_RESIZE_CURSOR:
							adjust(bounds, min, 0, pt.y - dragOffsetY,
									pt.x + (dragWidth - dragOffsetX)
											- bounds.width,
									-(pt.y - dragOffsetY));
							break;
						case Cursor.SE_RESIZE_CURSOR:
							adjust(bounds, min, 0, 0,
									pt.x + (dragWidth - dragOffsetX)
											- bounds.width,
									pt.y + (dragHeight - dragOffsetY)
											- bounds.height);
							break;
						case Cursor.NW_RESIZE_CURSOR:
							adjust(bounds, min, pt.x - dragOffsetX,
									pt.y - dragOffsetY, -(pt.x - dragOffsetX),
									-(pt.y - dragOffsetY));
							break;
						case Cursor.SW_RESIZE_CURSOR:
							adjust(bounds, min, pt.x - dragOffsetX, 0,
									-(pt.x - dragOffsetX),
									pt.y + (dragHeight - dragOffsetY)
											- bounds.height);
					}
					if (!bounds.equals(startBounds))
					{
						int minScreenY = ScreenTools.getMinScreenY();
						if (bounds.y < minScreenY)
						{
							int delta = minScreenY - bounds.y;
							bounds.y = minScreenY;
							bounds.height -= delta;
						}
						resizeCom.setBounds(bounds);
						resizeCom.validate();
					}
				}
			}
		});

		dragCom.addMouseListener(new MouseAdapter()
		{
			FlatDialog dialog = null;
			FlatFrame frame = null;
			boolean b;

			@Override
			public void mousePressed(MouseEvent e)
			{
				if (cursorIndex != 0)
				{
					movable = false;
					dragOffsetX = e.getX();
					dragOffsetY = e.getY();
					dragWidth = resizeCom.getWidth();
					dragHeight = resizeCom.getHeight();

					if (resizeCom instanceof FlatDialog)
					{
						dialog = (FlatDialog) resizeCom;
						b = dialog.isRounded();
						if (b)
							dialog.setRounded(false);
					} else if (resizeCom instanceof FlatFrame)
					{
						frame = (FlatFrame) resizeCom;
						b = frame.isRounded();
						if (b)
							frame.setRounded(false);
					}
				}
			}

			@Override
			public void mouseReleased(MouseEvent e)
			{
				if (!movable)
				{
					if (resizeCom instanceof FlatDialog && dialog != null)
						dialog.setRounded(b);
					else if (resizeCom instanceof FlatFrame && frame != null)
						frame.setRounded(b);
				}
				movable = true;
			}

			@Override
			public void mouseEntered(MouseEvent ev)
			{
				mouseMoved(ev);
			}

			@Override
			public void mouseExited(MouseEvent ev)
			{
				if (ev.getSource() instanceof Window && savedCursor != null)
				{
					Window w = (Window) ev.getSource();
					w.setCursor(savedCursor);
					savedCursor = null;
				}
			}
		});
	}

	private static void adjust(Rectangle bounds, Dimension min, int deltaX,
			int deltaY, int deltaWidth, int deltaHeight)
	{
		bounds.x += deltaX;
		bounds.y += deltaY;
		bounds.width += deltaWidth;
		bounds.height += deltaHeight;
		if (min != null)
		{
			if (bounds.width < min.width)
			{
				int correction = min.width - bounds.width;
				if (deltaX != 0)
					bounds.x -= correction;
				bounds.width = min.width;
			}
			if (bounds.height < min.height)
			{
				int correction = min.height - bounds.height;
				if (deltaY != 0)
					bounds.y -= correction;
				bounds.height = min.height;
			}
		}
	}

	private static int calculateCorner(Component c, int x, int y)
	{
		int xPosition = calculatePosition(x, c.getWidth());
		int yPosition = calculatePosition(y, c.getHeight());
		if (xPosition == -1 || yPosition == -1)
			return -1;
		return yPosition * 5 + xPosition;
	}

	private static int getCursor(int corner)
	{
		if (corner == -1)
			return 0;
		return cursorMapping[corner];
	}

	private static int calculatePosition(int spot, int width)
	{
		if (spot < BORDER_DRAG_THICKNESS)
			return 0;
		if (spot < CORNER_DRAG_WIDTH)
			return 1;
		if (spot >= (width - BORDER_DRAG_THICKNESS))
			return 4;
		if (spot >= (width - CORNER_DRAG_WIDTH))
			return 3;
		return 2;
	}
}
